package com.espirit.eap.manager;

import java.io.Serializable;
import java.util.Locale;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.Table;

import com.espirit.EntityMapping;
import com.googlecode.cswish.annotation.QLFormula;
import com.googlecode.cswish.model.Saas;
import com.googlecode.cswish.util.bean.ModelUtil;
import com.opensymphony.xwork2.ActionContext;

@Entity
@Table(indexes={@Index(name="saas_idx_resKey", columnNames={"resKey"} )}, appliesTo = "saas_i18n")
@BatchSize(size=10)
public class I18n extends EntityMapping implements Serializable, Saas {

	private static final long serialVersionUID = 4772801844542142788L;
	private String key;
	private String enName;
	private String cnName;
	@JsonIgnore
	private String belongTo;
	
	public I18n() {
		super();
	}

	public I18n(String key, String enName, String cnName) {
		super();
		this.key = key;
		this.enName = enName;
		this.cnName = cnName;
	}

	@JsonIgnore
	private Integer sid;
	public Integer getSid() {
		return sid;
	}

	public void setSid(Integer sid) {
		this.sid = sid;
	}

	@Column(name="resKey", nullable=false)
	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}
	
//	@OneToMany(targetEntity = I18n.class,cascade = CascadeType.ALL,fetch=FetchType.LAZY,mappedBy = "parent")
//	public List<I18n> getChildren() {
//		if (children == null) {
//			children = new ArrayList<I18n>();
//		}
//		return children;
//	}
//
//	public void setChildren(List<I18n> children) {
//		this.children = children;
//	}
	
	public String getEnName() {
		return enName;
	}

	public void setEnName(String enName) {
		this.enName = enName;
	}

	public String getCnName() {
		return cnName;
	}

	public void setCnName(String cnName) {
		this.cnName = cnName;
	}
	
	@Transient
	@JsonIgnore
	@QLFormula(value = ":calculateCurNameProperty", type=String.class)
	public String getName() {
		ActionContext ctx = ActionContext.getContext();
		Locale locale = ctx == null ? null : ctx.getLocale();
		return getNameByLocale(locale);
	}
	
	public void setName(String name) {
		ActionContext ctx = ActionContext.getContext();
		Locale locale = ctx == null ? null : ctx.getLocale();
		String language = locale == null ? "zh" : locale.getLanguage();
		if ("zh".equals(language) || "cn".equals(language)) {
			setCnName(name);
		} else {
			setEnName(name);
		}
	}
	
	@Transient
	public String getBelongTo() {
		return belongTo;
	}

	public void setBelongTo(String belongTo) {
		this.belongTo = belongTo;
	}

	@Transient
	@JsonIgnore
	public String getNameByLocale(String language) {
		if ("zh".equals(language) || "cn".equals(language)) {
			return cnName;
		} else if ("en".equals(language)) {
			return enName;
		} else {
			// simple property can avoid the security issue
			return (String) ModelUtil.getSimpleProperty(this, language + "Name");
		}
	}
	
	@Transient
	@JsonIgnore
	public String getNameByLocale(Locale locale) {
		if (locale == null) {
			ActionContext ctx = ActionContext.getContext();
			locale = ctx == null ? null : ctx.getLocale();
		}
		
		// default is the Chinese language
		String language = locale == null ? "zh" : locale.getLanguage();
		return getNameByLocale(language);
	}
	
	public static String calculateCurNameProperty() {
		return calculateNamePropertyByLocale(null);
	}
	
	public static String calculateNamePropertyByLocale(Locale locale) {
		if (locale == null) {
			ActionContext ctx = ActionContext.getContext();
			locale = ctx == null ? null : ctx.getLocale();
		}
		
		// default is the Chinese language
		String language = locale == null ? "zh" : locale.getLanguage();
		if ("zh".equals(language) || "cn".equals(language)) {
			return "cnName";
		} else if ("en".equals(language)) {
			return "enName";
		} else {
			return language + "Name";
		}
	}

	private final static String[] gatherProperties = new String[]{"key"};
	//@Override
	public String[] gatherProperties() {
		return gatherProperties;
	}
	

	public I18n cloneIngoreId() {
		I18n i18n = new I18n();
		i18n.setKey(key);
		i18n.setCnName(cnName);
		i18n.setEnName(enName);
		
		return i18n;
	}
	public I18n cloneWithId() {
		I18n i18n = cloneIngoreId();
		i18n.setId(getId());
		return i18n;
	}
	
	public boolean equalsByName(I18n i18n) {
		if (i18n == null) {
			return false;
		}
		
		return StringUtils.equals(getCnName(), i18n.getCnName()) 
			&& StringUtils.equals(getEnName(), i18n.getEnName());
	}

	public String convertKeyToName() {
		String text;
		if (key == null || key.length() == 0) {							// short key
			text = "";
		} else {
			StringBuilder sb = new StringBuilder();
			String coreKey = guessCoreKey();
			
			//  upper case --> space + upper case
			char c = coreKey.charAt(0);
			if (!isUpperCase(c)) {
				sb.append(Character.toUpperCase(c));
			} else {
				sb.append(c);
			}
			for (int i = 1, maxIndex = coreKey.length() - 1; i <= maxIndex; i++) {
				c = coreKey.charAt(i);
				
				if (isUpperCase(c)) {
					// the following char is low case, insert blank to let current char follow the next low case
					if (i < maxIndex && !isUpperCase(coreKey.charAt(i + 1))) {
						sb.append(' ');
					// previous char is low case (except the first char), insert blank to separate current char
					} else if (i > 1 && !isUpperCase(coreKey.charAt(i - 1))) {
						sb.append(' ');
					}
					sb.append(c);
				} else {
					sb.append(c);
				}
			}

			text = sb.toString();
		}
		return text;
	}
	
	private String guessCoreKey() {
		int index = key.lastIndexOf('.');
		String coreKey = key.substring(index + 1);
		boolean useNextBlock = false;
		if (StringUtils.isNumeric(coreKey)) {
			useNextBlock = true;
		} else if (key.startsWith("menu.")) {
			if ("search".equals(coreKey) || "_search".equals(coreKey)) {	// TODO: more method
				useNextBlock = true;
			}
		} else {
			// TODO:
		}
		
		if (useNextBlock) {
			int begin = key.lastIndexOf('.', index - 1);
			if (begin < index) {
				coreKey = key.substring(begin + 1, index);
			}
		}
		return coreKey;
	}

	private boolean isUpperCase(char c) {
		// treat '.' as upper case
		return 'A' <= c && c <= 'Z';
	}

	public void updateTo(I18n another) {
		another.setCnName(getCnName());
		another.setEnName(getEnName());
		another.setName(getName());
	}
}